package com.oneinlet.client.handler

import com.oneinlet.AppConf
import com.oneinlet.Constant
import com.oneinlet.Pandora
import com.oneinlet.client.ClientChannelManage
import io.netty.bootstrap.Bootstrap
import io.netty.buffer.Unpooled
import io.netty.channel.Channel
import io.netty.channel.ChannelHandlerContext
import io.netty.channel.SimpleChannelInboundHandler
import io.netty.handler.timeout.IdleState
import io.netty.handler.timeout.IdleStateEvent
import org.slf4j.LoggerFactory

/**
 * Created by WangZiHe on 2019/1/9
 * QQ/WeChat:648830605
 * QQ-Group:368512253
 * Blog:www.520code.net
 * Github:https://github.com/yancheng199287
 */

class YClientHandler(private val bootstrap: Bootstrap) : SimpleChannelInboundHandler<Pandora>() {


    private val logger = LoggerFactory.getLogger(this.javaClass)
    override fun channelActive(ctx: ChannelHandlerContext?) {
        val pandora = Pandora()
        pandora.msgType = Pandora.MsgType.REQUEST_AUTH.type
        pandora.content = AppConf.accessKey
        pandora.data = ByteArray(512)
        pandora.channelId = ctx!!.channel().id().asLongText()
        ctx.writeAndFlush(pandora)
    }

    override fun channelRead0(ctx: ChannelHandlerContext, pandora: Pandora) {

        when (pandora.msgType) {

            Pandora.MsgType.HEARTBEAT_PACKET.type -> {
                logger.debug("收到服务端的心跳数据包")
            }

            Pandora.MsgType.SUCCESS_AUTH.type -> {
                ClientChannelManage.YCServerChannel = ctx.channel()
                logger.info("YClient 连接 YCServer 成功！")
            }
            Pandora.MsgType.FAILED_AUTH.type -> {
                ctx.close()
                logger.warn("YClient 连接 YCServer 失败！")
            }

            Pandora.MsgType.DISCONNECT_FROM_APPSERVER_TO_YCLINET.type -> {
                ClientChannelManage.getClientProxyChannel(pandora.channelId)
            }
            else -> {
                // 注意 有服务 是开了多个端口进行访问的，对于mysql，可以连接，但是打不开表，是由于打开表的时候又启动新的端口，
                // 所以当使用长连接，发现通道id变化了（一般长连接通道只有一个，所以不会变） 对于mysql会变，那么就需要在这里重新连接startProxyClient
                val clientProxyChannel = ClientChannelManage.getClientProxyChannel(pandora.channelId)
                if (clientProxyChannel != null && clientProxyChannel.isActive) {
                    clientProxyChannel.writeAndFlush(Unpooled.copiedBuffer(pandora.data))
                } else {
                    startProxyClient(ctx.channel(), pandora)
                }

            }
        }

    }


    private fun startProxyClient(serverChannel: Channel, pandora: Pandora) {

        val f = bootstrap.connect(AppConf.getStringValue()["serverHost"], AppConf.getStringValue()["serverPort"]!!.toInt())

        f.addListener { future ->
            if (future.isSuccess) {
                val channel = f.channel()
                // 把通道id关联到我们这个连接的通道，在这个连接通道读取数据方法可以通过缓存找到这个连接通道，通过连接通道找到attr附加上通道id
                channel.attr(Constant.channelIdAttributeKey).set(pandora.channelId)
                ClientChannelManage.addClientProxyChannel(channel.id().asLongText(), channel)


                ClientChannelManage.addClientProxyChannel(pandora.channelId, channel)
                logger.debug("代理客户端连接远程目标服务成功，正在尝试请求发送消息...")


                channel.writeAndFlush(Unpooled.copiedBuffer(pandora.data))

                //缓存本次发送的通道，下次需要通过该通道读取
            } else {
                logger.warn("Pandora-Client 请求本地服务无响应，请检查对应端口  的服务是否成功开启，服务端即将断开本次连接！")
                pandora.msgType = Pandora.MsgType.DISCONNECT_FROM_YCLINET_TO_YCSERVER.type
                pandora.data = Unpooled.EMPTY_BUFFER.array()
                pandora.content = "客户端请求本地服务无响应，申请断开连接！"
                serverChannel.writeAndFlush(pandora)
                serverChannel.close()
                bootstrap.config().group().shutdownGracefully()
            }
        }
    }

    //IdleStateHandler放在入口，它会自动检测整个链条的handler，回调带有userEventTriggered的方法
    override fun userEventTriggered(ctx: ChannelHandlerContext?, event: Any?) {
        // logger.warn("心跳检测事件触发，可能断线，触发重连机制")
        if (event is IdleStateEvent) {
            when {
                event.state() == IdleState.READER_IDLE ->
                    return
                //logger.warn("===服务器端===(READER_IDLE 读超时)")
                event.state() == IdleState.WRITER_IDLE -> {
                    //logger.warn("===服务器端===(WRITER_IDLE 写超时)")
                    val channel = ctx!!.channel()
                    if (channel.isActive) {
                        channel.writeAndFlush(Constant.makeHeartBeatPack(channel.id().asLongText())).isSuccess
                    }
                }
                event.state() == IdleState.ALL_IDLE ->
                    //logger.warn("===服务器端===(ALL_IDLE 总超时)")
                    return
            }
        }
    }


    override fun exceptionCaught(ctx: ChannelHandlerContext?, cause: Throwable?) {
        logger.error("发生一个异常，通道:${ctx!!.channel()}", cause)
    }


    override fun channelInactive(ctx: ChannelHandlerContext?) {
        logger.warn("通道已经关闭 ${ctx!!.channel()}")
    }


}